package view
{
    import flash.display.*;
    import flash.events.*;
    import flash.text.*;

    import model.GameModelLocator;
    import model.vo.GameVo;
    import utils.*;
    import view.*;

    public class Game extends Sprite
    {
        private static const enemyBornTimeLine:Array = [
            [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 3, 1, 3, 1, 3,
             0, 0, 0, 1, 1, 2, 3, 2, 3, 1, 1, 1, 1, 1, 2, 3, 2, 3, 2, 3,
             0, 0, 0, 2, 3, 2, 3, 2, 3, 1, 1, 1, 2, 3, 2, 3, 2, 3, 2, 3,
             1, 2, 1, 3, 1, 2, 1, 3, 1, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
             1, 2, 1, 3, 1, 2, 1, 3, 1, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
             2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3],

            [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
             4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
             4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3],

            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],

            [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
             4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
             4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3],

            [0, 0, 0, 1, 1, 1, 4, 5, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
             4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
             4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
             4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3]];

        private var timer:Time;
        private var pos:int;
        private var gameVo:GameVo;
        private var background:Background;
        private var healthText:TextField;
        private var airplane:Airplane;
        private var boss:Boss;
        private var bullets:Array;
        private var enemies:Array;
        private var enemyBullets:Array;

        public function Game()
        {
            timer = new Time();
            pos = 0;
            gameVo = GameModelLocator.getInstance().gameVo;

            boss = new Boss(500, 5);
            boss.x = 0;
            boss.y = -350;
            boss.visible = false;
            boss.display();
            addChild(boss);

            airplane = new Airplane();
            airplane.display();
            addChild(airplane);
            bullets = new Array();

            background = new Background();
            background.display();
            addChild(background);
            
            enemies = new Array();
            enemyBullets = new Array();

            healthText = new TextField();
            healthText.x = -200;
            healthText.y = 180;
            healthText.textColor = 0xFF0000;
            setHP();
            healthText.selectable = false;
            addChild(healthText);

            addEventListener(MouseEvent.MOUSE_DOWN, start);
        }

        public function start(e:Event):void
        {
            removeEventListener(MouseEvent.MOUSE_DOWN, start);
            addEventListener(MouseEvent.MOUSE_DOWN, beginFire);
            addEventListener(MouseEvent.MOUSE_UP, stopFire);
            addEventListener(Event.ENTER_FRAME, update);
            timer.start();
            gameVo.start();
            pos = 0;
        }

        public function stop():void
        {
            removeEventListener(MouseEvent.MOUSE_DOWN, beginFire);
            removeEventListener(MouseEvent.MOUSE_UP, stopFire);
            removeEventListener(Event.ENTER_FRAME, update);
            reset();
        }

        public function reset():void
        {
            boss.reset(500, 5);
            boss.x = 0;
            boss.y = -350;
            boss.visible = false;
            boss.display();

            airplane.reset();
            setHP();
            while(bullets.length > 0)
            {
                removeChild(bullets[0]);
                bullets.shift();
            }
            while(enemies.length > 0)
            {
                removeChild(enemies[0]);
                enemies.shift();
            }
            while(enemyBullets.length > 0)
            {
                removeChild(enemyBullets[0]);
                enemyBullets.shift();
            }
            addEventListener(MouseEvent.MOUSE_DOWN, start);
        }

        public function beginFire(e:Event):void
        {
            airplane.fire = true;
        }

        public function stopFire(e:Event):void
        {
            airplane.fire = false;
        }

        public function updatePlayer(t:Number):void
        {
            airplane.update(t, mouseX, mouseY);
            var hit:Boolean = false;
            for(var i:int=0; i<enemies.length; i++)
            {
                if(enemies[i].visible)
                {
                    if((enemies[i].x - airplane.x) * (enemies[i].x - airplane.x) +
                       (enemies[i].y - airplane.y) * (enemies[i].y - airplane.y) <= 1600)
                    {
                        hit = true;
                        airplane.healthPoint -= enemies[i].attackPoint;
                        setHP();
                        if(airplane.healthPoint <= 0)
                        {
                            airplane.display();
                            stop();
                            return;
                        }
                    }
                }
            }
            for(i=0; i<enemyBullets.length; i++)
            {
                if(enemyBullets[i].visible)
                {
                    if((enemyBullets[i].x - airplane.x) * (enemyBullets[i].x - airplane.x) +
                       (enemyBullets[i].y - airplane.y) * (enemyBullets[i].y - airplane.y) <= 400)
                    {
                        hit = true;
                        enemyBullets[i].visible = false;
                        airplane.healthPoint -= 5;
                        setHP();
                        if(airplane.healthPoint <= 0)
                        {
                            stop();
                            return;
                        }
                    }
                }
            }
            airplane.hit = hit;

            // Update player's bullets
            for(i=0; i<bullets.length; i++)
            {
                bullets[i].update(t);
            }
            // Remove player's bullets
            while(bullets.length > 0 && bullets[0].y < -220)
            {
                removeChild(bullets[0]);
                bullets.shift();
            }
            // Add player's bullets
            if(airplane.fire)
            {
                var bullet:Bullet = new Bullet(0, -0.4);
                bullet.x = airplane.x - 10;
                bullet.y = airplane.y + 10 * Math.random();
                bullet.display();
                addChild(bullet);
                bullets.push(bullet);

                bullet = new Bullet(0, -0.4);
                bullet.x = airplane.x + 10;
                bullet.y = airplane.y + 10 * Math.random();
                bullet.display();
                addChild(bullet);
                bullets.push(bullet);
            }
        }

        public function updateEnemy(t:Number, total:Number):void
        {
            for(var i:int=0; i<enemies.length; i++)
            {
                var hit:Boolean = false;
                enemies[i].update(t);
                if(enemies[i].visible)
                {
                    for(var j:int=0; j<bullets.length; j++)
                    {
                        if(bullets[j].visible == true &&
                           ((enemies[i].x - bullets[j].x) * (enemies[i].x - bullets[j].x) +
                            (enemies[i].y - bullets[j].y) * (enemies[i].y - bullets[j].y) <= 400))
                        {
                            hit = true;
                            bullets[j].visible = false;
                            enemies[i].healthPoint -= airplane.attackPoint;
                            if(enemies[i].healthPoint <= 0)
                            {
                                enemies[i].visible = false;
                                break;
                            }
                        }
                    }
                }
                enemies[i].hit = hit;
            }
            while(enemies.length > 0 && enemies[0].y > 200)
            {
                removeChild(enemies[0]);
                enemies.shift();
            }
            for(i=0; i<enemyBullets.length; i++)
            {
                enemyBullets[i].update(t);
            }
            // Remove enemy's bullets
            while(enemyBullets.length > 0 &&
                  (enemyBullets[0].x < -200 || enemyBullets[0].x > 200 ||
                   enemyBullets[0].y < -220 || enemyBullets[0].y > 200))
            {
                removeChild(enemyBullets[0]);
                enemyBullets.shift();
            }
            if(total / 1000 > pos)
            {
                pos++;
                if(pos < 120)
                {
                    if(enemyBornTimeLine[gameVo.level-1][pos] > 0)
                    {
                        var enemy:Enemy = new Enemy(enemyBornTimeLine[gameVo.level-1][pos]);
                        enemy.x = 360 * Math.random() - 180;
                        enemy.y = -300;
                        enemy.display();
                        addChild(enemy);
                        enemies.push(enemy);
                    }
                }
                if(enemies.length > 0)
                {
                    for(i=0; i<gameVo.level; i++)
                    {
                        createEnemyBullet();
                    }
                }
            }
        }

        public function updateBoss(t:Number, total:Number):void
        {
            boss.update(t, airplane.x, airplane.y);
            if(boss.moveMode == Boss.MOVE_DOWN && boss.y > -150)
            {
                boss.moveMode = Boss.MOVE_NORMAL;
            }
            if(boss.moveMode == Boss.MOVE_NORMAL)
            {
                var hit:Boolean = false;
                for(var i:int=0; i<bullets.length; i++)
                {
                    if(bullets[i].visible == true &&
                       ((boss.x - bullets[i].x) * (boss.x - bullets[i].x) +
                        (boss.y - bullets[i].y) * (boss.y - bullets[i].y) <= 400))
                    {
                        hit = true;
                        bullets[i].visible = false;
                        boss.healthPoint -= airplane.attackPoint;
                        if(boss.healthPoint <= 0)
                        {
                            stop();
                            return;
                        }
                    }
                }
                boss.hit = hit;
                i = 100 * Math.random();
                if(i > 80)
                {
                    var dx:Number = airplane.x - (boss.x + boss.gunX);
                    var dy:Number = airplane.y - (boss.y + boss.gunY);
                    var l:Number = Math.sqrt((dx*dx) + (dy*dy));
                    var bullet:Bullet = new Bullet(0.2 * dx / l, 0.2 * dy / l, 4, 0xFF0000);
                    bullet.x = boss.x + boss.gunX;
                    bullet.y = boss.y + boss.gunY;
                    bullet.display();
                    addChild(bullet);
                    enemyBullets.push(bullet);
                }
                if((boss.x - airplane.x) * (boss.x - airplane.x) +
                   (boss.y - airplane.y) * (boss.y - airplane.y) <= 4900)
                {
                    airplane.hit = true;
                    airplane.healthPoint -= boss.attackPoint;
                    setHP();
                    if(airplane.healthPoint <= 0)
                    {
                        airplane.display();
                        stop();
                        return;
                    }
                }
                else
                {
                    airplane.hit = false;
                }
            }
        }

        public function update(e:Event):void
        {
            var t:Number = timer.interval();
            timer.next();
            updatePlayer(t);
            var total:Number = timer.total();
            updateEnemy(t, total);
            if(total / 1000 > 125)
            {
                boss.visible = true;
                boss.display();
                updateBoss(t, total);
            }
        }

        public function createEnemyBullet():void
        {
            if(enemies.length > 0)
            {
                var i:int = (enemies.length-1) * Math.random();
                if(enemies[i].visible)
                {
                    var dx:Number = airplane.x - enemies[i].x;
                    var dy:Number = airplane.y - enemies[i].y;
                    var l:Number = Math.sqrt((dx*dx) + (dy*dy));
                    var bullet:Bullet = new Bullet(0.2 * dx / l, 0.2 * dy / l, 4, 0xFF0000);
                    bullet.x = enemies[i].x;
                    bullet.y = enemies[i].y;
                    bullet.display();
                    addChild(bullet);
                    enemyBullets.push(bullet);
                }
            }
        }

        public function setHP():void
        {
            healthText.htmlText = "<b><font size='12'>HP: " + airplane.healthPoint + "</font></b>";
        }
    }
}
